
// 111111111 
new Promise((resolve,reject)=>{
    setTimeout(() => {
        // resolve 返回成功相应
        resolve('hello')
    }, 10);
})
.then(res=>{
    return new Promise(resolve=>{
        setTimeout(()=>{
            resolve(res+'lagou')
            },10)
    })
}).then(res=>{
    setTimeout(() => {
        console.log( res + ' i LOVE you' );
    }, 10);
})

// 2222222222
const fp = require("lodash/fp");

//数组
// horsepower 马力，dollar_ value价格，in_ stock库存
const cars = [
  { name: "Ferrari FF", horsepower: 660, dollar_value: 700000, in_stock: true },
  {
    name: " Spyker C12 Zagato",
    horsepower: 650,
    dollar_value: 648000,
    in_stock: false,
  },
  {
    name: "Jaguar XKR-S",
    horsepower: 550,
    dollar_value: 132000,
    in_stock: false,
  },
  { name: "audio R8", horsepower: 525, dollar_value: 114200, in_stock: false },
  {
    name: "Aston Martin One-77",
    horsepower: 750,
    dollar_value: 1850000,
    in_stock: true,
  },
  {
    name: "Pagani Huayra",
    horsepower: 700,
    dollar_value: 1300000,
    in_stock: false,
  },
];
/* 
    用flowRight 改造函数
    1.fp.prop() 接收两个参数，先进行柯里化
    2.flowRight组合函数
*/
const prop = fp.curry(fp.prop);
let newFunc = fp.flowRight(prop("in_stock"), fp.last);
console.log(newFunc(cars)); // false

// 实现firstName
let getFirstName = fp.flowRight(prop("name"), fp.first);
console.log(getFirstName(cars)); // false

/* 
 _averge
 柯里化fp.map方法 
*/
let _average = function (xs) {
    return fp.reduce(fp.add, 0, xs) / xs.length
  } // <- 无须改动
let map = fp.curry((fn, cars) => fp.map(fn, cars));
let newAverageDollarValue = fp.flowRight(
  _average,
  map((car) => {
    return car.dollar_value;
  })
);
console.log(newAverageDollarValue(cars));

/*
  练习4: 
  实现 sanitizeNames() 函数，要求使用 fp.flowRight()
  把 返回一个下划线连接的小写字符串，
  把数组中的 name 转换为这种形式： 例如：sanitizeNames(["Hello World"]) => ["hello_world"]
*/
// 把非字母数字替换为下划线
let _underscore = fp.replace(/\W+/g, '_') // <--无须改动
// _.map  
// _.toLower
// _underscore
// let log = (v)=>{
//     console.log(v);
//     return v
// }
const sanitizeNames = flowRight(_underscore,map((item)=>{return fp.toLower(item)}) )

console.log(sanitizeNames(['HELLO WORLD']));


// 3333
const fp = require("lodash/fp");
class Container {
  static of(value) {
    return new Container(value);
  }

  constructor(value) {
    this._value = value;
  }
  map(fn) {
    return Container.of(fn(this._value));
  }
}
class Maybe {
  static of(x) {
    return new Maybe(x);
  }
  isNothing() {
    return this._value === null || this._value === undefined;
  }

  constructor(x) {
    this._value = x;
  }

  map(fn) {
    return this.isNothing() ? this : Maybe.of(fn(this._value));
  }
}

/*
  练习1: 
  实现函数 ex1 
  使用 fp.add(x, y) 和 fp.map(f, x)
  让函子里的值 增加1
*/
// 1.创建一个函子
let maybe = Maybe.of([5, 6, 1]);
// 2.实现 ex1 函数
let add1 = (arr) => {
  return fp.map((item) => {
    return fp.add(item, 1);
  }, arr);
};
// 3.调用测试
console.log(maybe.map(add1)); // Maybe { _value: [ 6, 7, 2 ] }

/*
  练习2:
  实现 ex2 函数
  函数中使用 fp.first 获取列表的第一个元素
*/
// 1.生成一个函子
let xs = Container.of(["do", "ray", "me", "fa", "so", "la", "ti", "do"]);

// 2.实现 ex2
let ex2 = (arr)=>{
    return fp.first(arr)
}
// 3.测试打印
console.log( xs.map(ex2) )  // Container { _value: 'do' }

/*
  练习3:
  实现 ex3 函数
  使用 safeProp 和 fp.first 找到 user 的名字的首字母
*/
let safeProp = fp.curry(function (x, o) {
  return Maybe.of(o[x]);
});
let user = { id: 2, name: "Albert" };

// 1.实现 ex3
let ex3 = ()=>{
   return safeProp('name',user).map(fp.first)
}
// 2.测试打印
console.log(ex3()); // Maybe { _value: 'A' }

/*
  练习4:
  实现 ex4 函数
  把参数转为整数,并且返回相应函子
  要求使用 Maybe 
*/
let maybe4 = Maybe.of('7R')
// 1.实现 ex4 函数
let ex4 = (v)=>{
    return parseFloat(v).toFixed(0) 
}
// 2.测试打印
console.log(maybe4.map(ex4)); // Maybe { _value: 7 }



// 手写promsie
/* 
    1.promise是一个类
    2.实列化这个类的时候需要传递一个执行器,且立即执行这个执行器,执行器接受两个参数,resolve,reject
    3.三种状态pedding,fulfilled,rejected
            pedding -> fulfilled
            pedding -> rejected
            只能改变一次
    4.  1.then()方法里去注册成功回调和失败回调,回调中带上相对应的参数，
        2.判断promise状态，如果是成功状态执行成功回调，如果是失败回到执行失败回调
        3.处理异步，promise状态未改变，说明reslov方法还没执行，所有保存回调函数，等到resolve方法执行时在执行
        4.一个promise对象可以调用多个then方法，需要把回调函数储存到数组中，等resolve执行时，遍历回调队列，依次调用
        5.then方法可以链式调用，返回一个promise，把上一个then方法返回的值传递下去
    --------------异步--------------
    1.then方法里面去注册成功和失败回调，保存下来。等待调用。
    2.等到执行器中的异步代码执行，异步代码执行时，返回结果，成功/失败回调得到结果后执行。
*/class RbPromise {
    constructor(executor){
        try{
            executor(this.resolve,this.reject)
        }catch(e){ //捕获构造器错误
            this.reject(e)
        }
    }
    // promise状态
    status='pedding'
    // 成功状态
    value = undefined
    // 失败原因
    reason = undefined
    // 成功回调队列
    fulfilledCallback = []
    // 失败回调队列
    rejectCallback = []
    // resolve方法
    resolve=(value)=>{
        if (this.status !== 'pedding') return
        this.status = 'fulfilled'
        this.value = value
        // 遍历成功回调队列
        while(this.fulfilledCallback.length){
            this.fulfilledCallback.shift()()
        }
    }
    // reject方法
    reject=(reason)=>{
        if (this.status !== 'pedding') return
        this.status = 'rejected'
        this.reason = reason
        // 遍历失败回调队列
        while(this.rejectCallback.length){
            this.rejectCallback.shift()()
        }
    }

    then=(onFulfilled,onRejected)=>{
        // 调用then方法的时候，可以传递参数，也可以不传递参数，不传递参数的时候默认返回一个上一个promise的返回值
        onFulfilled = onFulfilled?onFulfilled:value=>value
        onRejected = onRejected?onRejected:reason=>reason

        let promise2 = new RbPromise((resolve,reject)=>{ //执行器，立即执行
            if (this.status === 'fulfilled') {
                try{
                    setTimeout(()=>{ //new RbPromise执行完成后才会返回promise2，为了取到promise2，把resolvePromise方法放在异步代码块中
                        let x = onFulfilled(this.value) //得到成功回调返回值
                        
                        resolvePromise (promsie2, x, resolve, reject) 
    
                    },0)
                }catch(e){ //捕获成功回调的错误
                    reject(e)
                }

            }else if(this.status === 'rejected'){
                try{
                    setTimeout(()=>{ //new RbPromise执行完成后才会返回promise2，为了取到promise2，把resolvePromise方法放在异步代码块中
                        let x = onRejected(this.reason) //得到成功回调返回值
                        
                        resolvePromise (promsie2, x, resolve, reject) 
    
                    },0)
                }catch(e){ //捕获失败回调的错误
                    reject(e)
                }
                
            }else{ //处理异步：promise状态未改变，说明reslov方法还没执行，所有保存回调函数，等到resolve方法执行时在执行
                this.fulfilledCallback.push(()=>{
                    try{
                        setTimeout(()=>{ //new RbPromise执行完成后才会返回promise2，为了取到promise2，把resolvePromise方法放在异步代码块中
                            let x = onFulfilled(this.reason) //得到成功回调返回值
                            resolvePromise (promsie2, x, resolve, reject) 
                        },0)
                    }catch(e){ //捕获失败回调的错误
                        reject(e)
                    }
                })
                this.rejectCallback.push(()=>{
                    try{
                        setTimeout(()=>{ //new RbPromise执行完成后才会返回promise2，为了取到promise2，把resolvePromise方法放在异步代码块中
                            let x = onRejected(this.reason) //得到成功回调返回值
                            resolvePromise (promsie2, x, resolve, reject) 
                        },0)
                    }catch(e){ //捕获失败回调的错误
                        reject(e)
                    }
                })
            }
        })
        return promise2 //链式调用，返回一个新的promise对象
    }
    /* 
        1.接受一个参数,是一个回调函数
        2.不管promise的状态如何，都会执行
        3.返回值是一个promsie
    */
    finally=(callback)=>{
        let P = this.constructor;
        return this.then(
            value  => P.resolve(callback()).then(() => value),
            reason => P.resolve(callback()).then(() => { throw reason })
        );
    }
    /* 
        1.接受一个数组,数组元素可以是promise对象，也可以是普通值
        2.如果是普通值，直接加入返回结果数组中，如果是promise对象，等待执行完成。
        3.如果成功，把结果添加到返回结果集中，如果失败，直接reject。
        4.传入任务队列都执行成功后返回结果集
        5.返货结果是一个包含resluts的promsie对象
    */
    static all(array){
        let results = []; 
        let index = 0
        return new Promise((resolve,reject)=>{
            function addresult(i,res) {
                index++
                results[i] = res //按照任务队列索引保存结果，保证顺序
                if (index === array.length) { //任务执行完毕
                    resolve(results) 
                }
            }
            for (let i = 0; i < array.length; i++) {
                let cur = array[i]
               if ( cur instanceof RbPromise ) {
                   cur.then((res)=>{ addresult(i,res) },(reason)=>{ reject(reason)})
               }else{
                   addresult(i,array[i])
               }
            }
        })
    }
    /* 
        1.接收一个参数
        2.如果参数是普通对象，就包裹进primose中resolve出来，如果是promsie对象，直接返回这个值
    */
   static resolve(value){
    if (value instanceof RbPromise) return value
    new Promise((resolve,reject)=>{
            resolve(value)
        })
   }

}
/* 
    1.判断x是promise对象还是非promise对象
    2.是普通对象直接用resolve保存
    3.promise对象，查看状态是成功还是失败
    4.成功调用resolve，失败调用reject
*/
function resolvePromise (promsie2, x, resolve, reject) {
    if (promsie2 === x) { //阻止循环调用自身
        return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
    }
    if(x instanceof RbPromise){ //x是promise对象
        x.then(value=>resolve(value),reason=>reject(reason))
    }else{ //不是promise对象
        resolve(x)
    }
}
